﻿using System;
using System.Drawing;
using System.Runtime.InteropServices;
using ESRI.ArcGIS.ADF.BaseClasses;
using ESRI.ArcGIS.ADF.CATIDs;
using ESRI.ArcGIS.Controls;
using ESRI.ArcGIS.Geodatabase;
using System.Windows.Forms;
using ESRI.ArcGIS.Geometry;
using ESRI.ArcGIS.Carto;

namespace DIWAGIS.ApplicationLayer.EditorTools
{
    /// <summary>
    /// Command that works in ArcMap/Map/PageLayout
    /// </summary>
    [Guid("8b5e8a48-9b1c-4f0d-855e-6e7374b7f18b")]
    [ClassInterface(ClassInterfaceType.None)]
    [ProgId("DIWAGIS.ApplicationLayer.EditorTools.cmdIntersectLine")]
    public sealed class cmdIntersectLine:BaseCommand
    {
        #region COM Registration Function(s)
        [ComRegisterFunction()]
        [ComVisible(false)]
        static void RegisterFunction(Type registerType)
        {
            // Required for ArcGIS Component Category Registrar support
            ArcGISCategoryRegistration(registerType);

            //
            // TODO: Add any COM registration code here
            //
        }

        [ComUnregisterFunction()]
        [ComVisible(false)]
        static void UnregisterFunction(Type registerType)
        {
            // Required for ArcGIS Component Category Registrar support
            ArcGISCategoryUnregistration(registerType);

            //
            // TODO: Add any COM unregistration code here
            //
        }

        #region ArcGIS Component Category Registrar generated code
        /// <summary>
        /// Required method for ArcGIS Component Category registration -
        /// Do not modify the contents of this method with the code editor.
        /// </summary>
        private static void ArcGISCategoryRegistration(Type registerType)
        {
            string regKey = string.Format("HKEY_CLASSES_ROOT\\CLSID\\{{{0}}}", registerType.GUID);
            MxCommands.Register(regKey);
            ControlsCommands.Register(regKey);
        }
        /// <summary>
        /// Required method for ArcGIS Component Category unregistration -
        /// Do not modify the contents of this method with the code editor.
        /// </summary>
        private static void ArcGISCategoryUnregistration(Type registerType)
        {
            string regKey = string.Format("HKEY_CLASSES_ROOT\\CLSID\\{{{0}}}", registerType.GUID);
            MxCommands.Unregister(regKey);
            ControlsCommands.Unregister(regKey);
        }

        #endregion
        #endregion

        private IHookHelper m_hookHelper = null;
        private IEngineEditor m_engineEditor;
        private IWorkspaceEdit m_workspaceEdit;
        public IWorkspaceEdit workspaceEdit
        {
            get { return m_workspaceEdit; }
            set { m_workspaceEdit = value; }
        }
        public cmdIntersectLine()
        {
            //
            // TODO: Define values for the public properties
            //
            base.m_category = "Tạo giao điểm giữa hai đường"; //localizable text
            base.m_caption = "Tạo giao điểm giữa hai đường";  //localizable text 
            base.m_message = "Tạo giao điểm giữa hai đường";  //localizable text
            base.m_toolTip = "Tạo giao điểm giữa hai đường";  //localizable text
            base.m_name = "cmdIntersectLine";   //unique id, non-localizable (e.g. "MyCategory_MyCommand")

            try
            {
                //
                // TODO: change bitmap name if necessary
                //
                string bitmapResourceName = GetType().Name + ".bmp";
                base.m_bitmap = new Bitmap(GetType(), bitmapResourceName);
            }
            catch (Exception ex)
            {
                System.Diagnostics.Trace.WriteLine(ex.Message, "Invalid Bitmap");
            }
        }

        #region Overridden Class Methods

        /// <summary>
        /// Occurs when this command is created
        /// </summary>
        /// <param name="hook">Instance of the application</param>
        public override void OnCreate(object hook)
        {
            if (hook == null)
                return;

            try
            {
                m_hookHelper = new HookHelperClass();
                m_hookHelper.Hook = hook;
                m_engineEditor = new EngineEditorClass();
                if (m_hookHelper.ActiveView == null)
                    m_hookHelper = null;
            }
            catch
            {
                m_hookHelper = null;
            }

            if (m_hookHelper == null)
                base.m_enabled = false;
            else
                base.m_enabled = true;

            // TODO:  Add other initialization code
        }

        /// <summary>
        /// Occurs when this command is clicked
        /// </summary>
        public override void OnClick()
        {
            // TODO: Add cmdIntersectLine.OnClick implementation
            Intersect();
        }

        /// <summary>
        /// Split polyline theo tập điểm
        /// </summary>
        /// <param name="pFeature"></param>
        /// <param name="pSplitPoints"></param>
        private void SplitFeature(IFeature pFeature, IPointCollection pSplitPoints)
        {
            IEnumVertex pEnumVertex;
            IGeometryCollection pGeoColl;
            IPolycurve2 pPolyCurve;
            IEnumSplitPoint pEnumSplitPoint;
            int PartCount;

            IMap map = m_hookHelper.FocusMap;
            pFeature.Shape.Project(map.SpatialReference);
            //Split the found features, each split makes a new part
            pEnumVertex = pSplitPoints.EnumVertices;
            pPolyCurve = pFeature.Shape as IPolycurve2;
            pEnumSplitPoint = pPolyCurve.SplitAtPoints(pEnumVertex, true, true, -1);

            if (!pEnumSplitPoint.SplitHappened)
                return;

            //Convert each part to a new feature
            pGeoColl = pPolyCurve as IGeometryCollection;

            //make sure the new geometry is z aware, set a flag for later use.
            IGeometryDef fcGeoDef = CheckZGeometryDef((IFeatureClass)pFeature.Class);

            //Gán geo
            pFeature.Shape = BuildGeometry(pGeoColl.get_Geometry(0), fcGeoDef);
            //Cập nhật
            pFeature.Store();

            //Cập nhật cho các path còn lại
            for (PartCount = 1; PartCount < pGeoColl.GeometryCount; PartCount++)
            {
                IFeatureClass featureClass = pFeature.Class as IFeatureClass;
                IFeature newFeature = featureClass.CreateFeature();
                newFeature.Shape = BuildGeometry(pGeoColl.get_Geometry(PartCount), fcGeoDef);
                CopyAttributes(pFeature, newFeature);
                newFeature.Store();
            }
        }


        /// <summary>
        /// Sao chép thuộc tính
        /// </summary>
        /// <param name="pSourceFeature"></param>
        /// <param name="pDestinationFeature"></param>
        private void CopyAttributes(IFeature pSourceFeature, IFeature pDestinationFeature)
        {
            IField pField;
            IFields pFields;
            int FieldCount;

            pFields = pSourceFeature.Fields;
            for (FieldCount = 0; FieldCount < pFields.FieldCount; FieldCount++)
            {
                pField = pFields.get_Field(FieldCount);
                if (pField.Editable)
                {
                    if (!(pField.Type == esriFieldType.esriFieldTypeOID) && !(pField.Type == esriFieldType.esriFieldTypeGeometry))
                    {
                        pDestinationFeature.set_Value(FieldCount, pSourceFeature.get_Value(FieldCount));
                    }
                }
            }
        }

        /// <summary>
        /// Tạo IGeometry
        /// </summary>
        /// <param name="pSegColl"></param>
        /// <returns></returns>
        private IGeometry BuildGeometry(IGeometry origPartGeometry, IGeometryDef fcGeoDef)
        {
            //Check the type of geometry.
            IPolygon polygon = new PolygonClass();
            IPolyline polyline = new PolylineClass();
            IMultipoint multiPoint = new MultipointClass();
            object Missing = Type.Missing;
            IGeometryCollection geometryCollection = origPartGeometry as IGeometryCollection;

            //if the feature class is z aware set the flag to true.
            Boolean isZAware = true;
            IZAware zAware;
            if (fcGeoDef.HasZ == false)
            {
                isZAware = false;
            }
            switch (origPartGeometry.GeometryType)
            {
                case esriGeometryType.esriGeometryRing:
                    if (isZAware == true)
                    {
                        zAware = polygon as IZAware;
                        zAware.ZAware = true;
                    }
                    geometryCollection = polygon as IGeometryCollection;
                    geometryCollection.AddGeometry
                      (origPartGeometry, ref Missing, ref Missing);
                    break;

                case esriGeometryType.esriGeometryPath:
                    if (isZAware == true)
                    {
                        zAware = polyline as IZAware;
                        zAware.ZAware = true;
                    }
                    geometryCollection = polyline as IGeometryCollection;
                    geometryCollection.AddGeometry
                      (origPartGeometry, ref Missing, ref Missing);
                    break;

                case esriGeometryType.esriGeometryPoint:
                    if (isZAware == true)
                    {
                        zAware = multiPoint as IZAware;
                        zAware.ZAware = true;
                    }
                    geometryCollection = multiPoint as IGeometryCollection;
                    geometryCollection.AddGeometry
                    (origPartGeometry, ref Missing, ref Missing);
                    break;
                default:
                    break;
            }
            return geometryCollection as IGeometry;
        }

        private void Intersect()
        {
            try
            {
                IFeature pFeature1;
                IFeature pFeature2;
                IMultipoint pIntersectionPoints;
                IPointCollection pPointColl;
                //ITopologicalOperator pTopoOperator;

                //Lấy danh sách các line đang được chọn trên bản đồ
                IMap map = m_hookHelper.FocusMap;
                //pFeatureCursor = map.FeatureSelection as IFeatureCursor;
                IEnumFeature enu = map.FeatureSelection as IEnumFeature;
                enu.Reset();

                //Kiểm tra xem đã chọn 2 đối tượng line chưa?
                //if (pFeatureCursor == null)
                //{
                //    //Không có đối tượng được chọn
                //    MessageBox.Show("Không có đối tượng được chọn");
                //    return;
                //}

                //Trỏ đến feature đầu tiên
                pFeature1 = enu.Next();//.Shape as ITopologicalOperator;// pFeatureCursor.NextFeature() as ITopologicalOperator;

                //Kiểm tra xem đã chọn đối tượng 1 chưa?
                if (pFeature1 == null || pFeature1.Shape.GeometryType != esriGeometryType.esriGeometryPolyline)
                {
                    MessageBox.Show("Bạn chưa chọn đủ hai đối tượng dạng đường để tạo giao điểm", "Thông báo");
                    return;
                }

                //Trỏ đến feature 2
                pFeature2 = enu.Next();//pFeatureCursor.NextFeature();

                //Kiểm tra xem đã chọn đối tượng 2 chưa?
                if (pFeature2 == null || pFeature2.Shape.GeometryType != esriGeometryType.esriGeometryPolyline)
                {
                    MessageBox.Show("Bạn chưa chọn đủ hai đối tượng dạng đường để tạo giao điểm", "Thông báo");
                    return;
                }

                //Lấy các điểm giao nhau giữa hai đường thẳng
                pIntersectionPoints = ((ITopologicalOperator)(pFeature1.Shape)).Intersect(pFeature2.Shape, esriGeometryDimension.esriGeometry0Dimension) as IMultipoint;

                //Nếu không có điểm giao thông bao
                if (pIntersectionPoints == null)
                {
                    MessageBox.Show("Các 'Đường' được chọn không có điểm giao", "Thông báo");
                    return;
                }

                pPointColl = pIntersectionPoints as IPointCollection; //QI

                //Split feature bởi danh sách các điểm
                SplitFeature(pFeature2, pPointColl);

                //Cắt chính line 1
                SplitFeature(pFeature1, pPointColl);
            }
            catch (Exception e)
            {
                MessageBox.Show("Tạo giao giữu hai đường thẳng.\n" + e.Message, "Thông báo lỗi");
                //m_engineEditor.AbortOperation();
            }
            finally
            {
                //Change the cursor shape to default.
                System.Windows.Forms.Cursor.Current = Cursors.Default;
            }
        }

        /// <summary>
        /// Kiểm tra kiểm geo - (Có dữ liệu Z hay không)
        /// </summary>
        /// <param name="featureClass"></param>
        /// <returns></returns>
        public IGeometryDef CheckZGeometryDef(IFeatureClass featureClass)
        {
            string shapeFieldName = featureClass.ShapeFieldName;
            IFields fields = featureClass.Fields;
            int geometryIndex = fields.FindField(shapeFieldName);
            IField field = fields.get_Field(geometryIndex);
            IGeometryDef geometryDef = field.GeometryDef;
            return geometryDef;
        }


        ///<summary>Use a polyline to cut a polygon.</summary>
        ///  
        ///<param name="polygon">An IPolygon interface that is the polygon to be split.</param>
        ///<param name="polyline">An IPolyline interface that is used to cut the input polygon.</param>
        ///   
        ///<returns>An IGeometryCollection with polygons resulting from the split.</returns>
        ///   
        ///<remarks></remarks>
        public ESRI.ArcGIS.Geometry.IGeometryCollection CutPolygon(ESRI.ArcGIS.Geometry.IPolygon polygon, ESRI.ArcGIS.Geometry.IPolyline polyline)
        {
            if (polygon == null || polyline == null)
            {
                return null;
            }
            ESRI.ArcGIS.Geometry.ITopologicalOperator4 topologicalOperator4 = polygon as ESRI.ArcGIS.Geometry.ITopologicalOperator4; // Dynamic Cast
            ESRI.ArcGIS.Geometry.IGeometryCollection geometryCollection = topologicalOperator4.Cut2(polyline);
            return geometryCollection;
        }

        #endregion

        public override bool Enabled
        {
            get
            {
                if (m_workspaceEdit != null)
                {
                    if (m_workspaceEdit.IsBeingEdited() == false)
                        return false;
                }
                else
                {
                    //check whether Editing 
                    if (m_engineEditor.EditState == esriEngineEditState.esriEngineStateNotEditing)
                    {
                        return false;
                    }
                }
                //conditions have been met so enable the tool
                return true;
            }
        }
    }
}
